!! Copyright (C) 2009,2010,2011,2012  Marco Restelli
!!
!! This file is part of:
!!   FEMilaro -- Finite Element Method toolkit
!!
!! FEMilaro is free software; you can redistribute it and/or modify it
!! under the terms of the GNU General Public License as published by
!! the Free Software Foundation; either version 3 of the License, or
!! (at your option) any later version.
!!
!! FEMilaro is distributed in the hope that it will be useful, but
!! WITHOUT ANY WARRANTY; without even the implied warranty of
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
!! General Public License for more details.
!!
!! You should have received a copy of the GNU General Public License
!! along with FEMilaro; If not, see <http://www.gnu.org/licenses/>.
!!
!! author: Marco Restelli                   <marco.restelli@gmail.com>

!>\brief
!!
!! This module provides a unified interface to the specific test case
!! modules for the Vlasov-Poisson equation. See
!! <tt>NS/mod_dgcomp_testcases</tt> for implementation details.
!<----------------------------------------------------------------------
module mod_testcases

!-----------------------------------------------------------------------

 use mod_messages, only: &
   mod_messages_initialized, &
   error,   &
   warning, &
   info

 use mod_kinds, only: &
   mod_kinds_initialized, &
   wp

 use mod_landau_damping_test, only: &
   mod_landau_damping_test_constructor, &
   mod_landau_damping_test_destructor,  &
   landau_damping_test_name        => test_name, &
   landau_damping_test_description => test_description, &
   landau_damping_coeff_init       => coeff_init

 use mod_bump_on_tail_test, only: &
   mod_bump_on_tail_test_constructor, &
   mod_bump_on_tail_test_destructor,  &
   bump_on_tail_test_name        => test_name, &
   bump_on_tail_test_description => test_description, &
   bump_on_tail_coeff_init       => coeff_init

 use mod_two_stream_inst_test, only: &
   mod_two_stream_inst_test_constructor, &
   mod_two_stream_inst_test_destructor,  &
   two_stream_inst_test_name        => test_name, &
   two_stream_inst_test_description => test_description, &
   two_stream_inst_coeff_init       => coeff_init

 use mod_poisson_test, only: &
   mod_poisson_test_constructor, &
   mod_poisson_test_destructor,  &
   poisson_test_name        => test_name, &
   poisson_test_description => test_description, &
   poisson_coeff_init       => coeff_init, &
   poisson_coeff_e          => coeff_e,    &
   poisson_coeff_p          => coeff_p

!-----------------------------------------------------------------------
 
 implicit none

!-----------------------------------------------------------------------

! Module interface

 public :: &
   mod_testcases_constructor, &
   mod_testcases_destructor,  &
   mod_testcases_initialized, &
   test_name,       & ! name of the test case
   test_description,& ! short description of the test case
   coeff_init, & ! initial condition for time dependent problems
   coeff_e,    & ! exact electric field
   coeff_p       ! exact electric potential

 private

!-----------------------------------------------------------------------

! Module types and parameters

 abstract interface
  pure function i_coeff_init(x,v) result(f0)
   import :: wp
   implicit none
   real(wp), intent(in) :: x(:,:), v(:,:)
   real(wp) :: f0(size(x,2))
  end function i_coeff_init
 end interface
 abstract interface
  pure function i_coeff_e(t,x) result(e)
   import :: wp
   implicit none
   real(wp), intent(in) :: t, x(:,:,:)
   real(wp) :: e(size(x,1),size(x,2))
  end function i_coeff_e
 end interface
 abstract interface
  pure function i_coeff_p(t,x) result(p)
   import :: wp
   implicit none
   real(wp), intent(in) :: t, x(:,:)
   real(wp) :: p(size(x,2))
  end function i_coeff_p
 end interface

! Module variables

 character(len=10000), protected              :: test_name
 character(len=10000), allocatable, protected :: test_description(:)
 procedure(i_coeff_init), pointer :: coeff_init => null()
 procedure(i_coeff_e   ), pointer :: coeff_e    => null()
 procedure(i_coeff_p   ), pointer :: coeff_p    => null()

 logical, protected ::               &
   mod_testcases_initialized = .false.
 character(len=*), parameter :: &
   this_mod_name = 'mod_testcases'

!-----------------------------------------------------------------------

contains

!-----------------------------------------------------------------------

 subroutine mod_testcases_constructor(testname,d)
  character(len=*), intent(in) :: testname
  integer, intent(in) :: d(2)

  integer :: i
  character(len=100+len_trim(testname)+len(test_name)) :: message
  character(len=*), parameter :: &
    this_sub_name = 'constructor'

   !Consistency checks ---------------------------
   if( (mod_messages_initialized.eqv..false.) .or. &
          (mod_kinds_initialized.eqv..false.) ) then
     call error(this_sub_name,this_mod_name, &
                'Not all the required modules are initialized.')
   endif
   if(mod_testcases_initialized.eqv..true.) then
     call warning(this_sub_name,this_mod_name, &
                  'Module is already initialized.')
   endif
   !----------------------------------------------

   casename: select case(testname)

    case(landau_damping_test_name)
     call mod_landau_damping_test_constructor(d)
     test_name = trim(landau_damping_test_name)
     allocate(test_description(size(landau_damping_test_description)))
     do i=1,size(test_description)
       test_description(i) = trim(landau_damping_test_description(i))
     enddo
     coeff_init => landau_damping_coeff_init

    case(bump_on_tail_test_name)
     call mod_bump_on_tail_test_constructor(d)
     test_name = trim(bump_on_tail_test_name)
     allocate(test_description(size(bump_on_tail_test_description)))
     do i=1,size(test_description)
       test_description(i) = trim(bump_on_tail_test_description(i))
     enddo
     coeff_init => bump_on_tail_coeff_init

    case(two_stream_inst_test_name)
     call mod_two_stream_inst_test_constructor(d)
     test_name = trim(two_stream_inst_test_name)
     allocate(test_description(size(two_stream_inst_test_description)))
     do i=1,size(test_description)
       test_description(i) = trim(two_stream_inst_test_description(i))
     enddo
     coeff_init => two_stream_inst_coeff_init

    case(poisson_test_name)
     call mod_poisson_test_constructor(d)
     test_name = trim(poisson_test_name)
     allocate(test_description(size(poisson_test_description)))
     do i=1,size(test_description)
       test_description(i) = trim(poisson_test_description(i))
     enddo
     coeff_init => poisson_coeff_init
     coeff_e    => poisson_coeff_e
     coeff_p    => poisson_coeff_p

    case default
     write(message,'(a,a,a)') 'Unknown test case "',trim(testname),'"'
     call error(this_sub_name,this_mod_name,message)
   end select casename

   write(message,'(a,a,a)') 'Test case: "',trim(test_name),'"'
   call info(this_sub_name,this_mod_name,message)

   mod_testcases_initialized = .true.
 end subroutine mod_testcases_constructor

!-----------------------------------------------------------------------
 
 subroutine mod_testcases_destructor()
  character(len=*), parameter :: &
    this_sub_name = 'destructor'
   
   !Consistency checks ---------------------------
   if(mod_testcases_initialized.eqv..false.) then
     call error(this_sub_name,this_mod_name, &
                'This module is not initialized.')
   endif
   !----------------------------------------------

   casename: select case(trim(test_name))
    case(landau_damping_test_name)
     call mod_landau_damping_test_destructor()
    case(bump_on_tail_test_name)
     call mod_bump_on_tail_test_destructor()
    case(two_stream_inst_test_name)
     call mod_two_stream_inst_test_destructor()
    case(poisson_test_name)
     call mod_poisson_test_destructor()
   end select casename

   ! nullify here the procedure pointers
   nullify( coeff_init , coeff_e , coeff_p )
   deallocate( test_description )

   mod_testcases_initialized = .false.
 end subroutine mod_testcases_destructor

!-----------------------------------------------------------------------

end module mod_testcases

